package LineCacheWriter

import (
    "fmt"
    "sync"
)

type LineCacheWriter struct {
    lines [][]byte
    w     int
    mu    sync.RWMutex
    seq   int64
    cnt   int

    cb_on_msg func(int64, []byte)
}

func New(maxLine int) *LineCacheWriter {
    x := &LineCacheWriter{}
    x.lines = make([][]byte, maxLine)
    return x
}

func (s *LineCacheWriter) OnMessage(f func(int64, []byte)) *LineCacheWriter {
    s.cb_on_msg = f
    return s
}

func (s *LineCacheWriter) Write(p []byte) (n int, err error) {
    line := make([]byte, len(p))
    copy(line, p)
    seq := s.append(line)

    if s.cb_on_msg != nil {
        s.cb_on_msg(seq, p)
    }
    return len(p), nil
}

func (s *LineCacheWriter) append(l []byte) int64 {
    s.mu.Lock()
    defer s.mu.Unlock()
    w := s.w
    s.lines[w] = nil
    s.lines[w] = l

    s.w += 1
    if s.w >= len(s.lines) {
        s.w = 0
    }

    seq := s.seq
    s.seq += 1
    if s.cnt < len(s.lines) {
        s.cnt += 1
    }
    return seq
}

func (s *LineCacheWriter) ReadLatestBackward(n int) [][]byte {
    s.mu.RLock()
    defer s.mu.RUnlock()

    var w int
    if s.w == 0 {
        w = len(s.lines) - 1
    } else {
        w = s.w - 1
    }

    var lst = make([][]byte, 0)
    for i := 0; i < n; i++ {
        line := s.lines[w]
        if line == nil {
            return lst
        }
        line_clone := make([]byte, len(line))
        copy(line_clone, line)
        lst = append(lst, line_clone)

        if w == 0 {
            w = len(s.lines) - 1
        } else {
            w -= 1
        }
    }
    return lst
}

func (s *LineCacheWriter) ReadForward(seq int64, max_line int) (int64, [][]byte) {
    s.mu.RLock()
    defer s.mu.RUnlock()

    seq_begin := seq
    if seq_begin < 0 {

        if max_line > s.cnt {
            max_line = s.cnt
        }
        seq_begin = s.seq - int64(max_line)
    }

    var lst = make([][]byte, 0)

    buf_oldest_seq := s.seq - int64(s.cnt)
    buf_newest_seq := s.seq - 1
    fmt.Println("buf_oldest_seq", buf_oldest_seq)
    fmt.Println("buf_newest_seq", buf_newest_seq)

    sel_oldest_seq := seq_begin
    sel_newest_seq := seq_begin + int64(max_line) - 1
    fmt.Println("sel_oldest_seq", sel_oldest_seq)
    fmt.Println("sel_newest_seq", sel_newest_seq)

    if sel_oldest_seq > buf_newest_seq {
        return -1, lst
    }
    if sel_newest_seq < buf_oldest_seq {
        return -1, lst
    }

    if sel_oldest_seq < buf_oldest_seq {
        sel_oldest_seq = buf_oldest_seq
    }
    if sel_newest_seq > buf_newest_seq {
        sel_newest_seq = buf_newest_seq
    }
    fmt.Println("sel_oldest_seq", sel_oldest_seq)
    fmt.Println("sel_newest_seq", sel_newest_seq)

    count := int(sel_newest_seq - sel_oldest_seq + 1)
    delta := int(buf_newest_seq - sel_oldest_seq + 1)
    fmt.Println("delta", delta, "s.w", s.w, "count", count)

    var idx int = -1

    if s.w >= delta {
        idx = s.w - delta
    } else {

        idx = len(s.lines) - (delta - s.w)
    }
    fmt.Println("idx", idx)

    w := idx
    for i := 0; i < count; i++ {
        line := s.lines[w]
        println("lines[", w, "]=", string(line))
        if line == nil {
            return -1, lst
        }
        line_clone := make([]byte, len(line))
        copy(line_clone, line)
        lst = append(lst, line_clone)

        if w == s.cnt-1 {
            w = 0
        } else {
            w += 1
        }
    }

    return sel_oldest_seq, lst
}
